function [U_final, S_final, V_final, nIter_final, objhistory_final] = LPFNMTF(X, rowK, colK, rowW, colW, options, U, S, V)
% Local Preserving Fast Nonnegative Matrix Tri-Factorization
% Input
%     X: (mFea x nSmp) data matrix 
%         mFea  ... number of words (vocabulary size)
%         nSmp  ... number of documents
%     rowK: number of row hidden factors
%     colK: number of column hidden factors
%     rowW: weight matrix of the affinity graph on features
%     colW: weight matrix of the affinity graph on samples
%     options: Structure holding all settings
%         options.rowLamda ... the regularization parameter. [default: 100]
%         options.colLamda ... the regularization parameter. [default: 100]
%
% You only need to provide the above four inputs.
%
% Output
%         F: nDim * rowK
%         G: nSmp * colK
%         obj: 
% 
% Problem:
%     min ||X - F S G'||^2 + rowLamda F' L_f F + colLamda G' L_s G
%     s.t. F \in {0,1}^{d,m}; G \in {0,1}^{n, c};
%
% Approximation:
%     min ||X - F S G'||^2 + rowLamda ||F - B_f Q_f||^2 + colLamda ||G - B_s Q_s||^2
%     s.t. F \in {0,1}^{d,m}; G \in {0,1}^{n, c}; Q_f' Q_f = I; Q_s' Q_s = I;
%
% 
% Note
% (1). F, G are cluster indicator matrices, Not relaxed other matrices
% (2). S is not constrained to be Non-negative, which means this algorithms
% can be applied when X has negative elements
% (3). The optimization of F, G are decoupled, which makes it is easy to
% optimization.
%
% [1]. Fast Nonnegative Matrix Tri-Factorization for Large-Scale Data
% Co-Clustering, Hua Wang, IJCAI, 2011
%


if min(min(X)) < 0
    error('Input should be nonnegative!');
end

if ~isfield(options,'error')
    options.error = 1e-5;
end
if ~isfield(options, 'maxIter')
    options.maxIter = [];
end

if ~isfield(options,'nRepeat')
    options.nRepeat = 10;
end

if ~isfield(options,'minIter')
    options.minIter = 30;
end

if ~isfield(options,'meanFitRatio')
    options.meanFitRatio = 0.1;
end

if ~isfield(options,'rowLamda')
    options.rowLamda = 100;
end

if ~isfield(options,'colLamda')
    options.colLamda = 100;
end

[nDim, nSmp] = size(X);

if isfield(options,'rowLamda_nDim') && options.rowLamda_nDim
    options.rowLamda = options.rowLamda * nDim;    
end

if isfield(options,'colLamda_nSmp') && options.colLamda_nSmp
    options.colLamda = options.colLamda * nSmp;    
end

if ~isfield(options,'Optimization')
    options.Optimization = 'Multiplicative';
end

if ~exist('U','var')
    U = [];
	S = [];
    V = [];
end

switch lower(options.Optimization)
    case {lower('Multiplicative')} 
        [U_final, S_final, V_final, nIter_final, objhistory_final] = LPFNMTF_Multi(X, rowK, colK, rowW, colW, options, U, S, V);
    otherwise
        error('optimization method does not exist!');
end